package com.example.android.raycast;

import android.content.Context;
import android.util.Log;

public class Level {
	public static int NUMBER_OF_LEVELS = 2;
	private int mCurrentLevelNumber = 1;
	
	public static int MAP_WIDTH = 10;
	public static int MAP_HEIGHT = 10;
	public static int TILE_SIZE = 64;

	public static int FLOOR_VALUE = 0;
	public static int OBJECT_START = 18;		
	public static int DOOR_START = 18;
	public static int DOOR_END = 19;
	public static int MONSTER_START = 20;
	public static int MONSTER_END = 22;
	public static int SPRITE_START = 23;
	public static int HEALTH_ITEM = 23;
	public static int AMMO_ITEM = 24;
	public static int LAMP_ITEM = 25;
	public static int SPRITE_END = 25;
	
	private int[][] mCurrentLevelMap;
	private int mCurrentLevelExitX;
	private int mCurrentLevelExitY;
	private int mCurrentLevelNumberMonstersAndSprites;
	private int mCurrentLevelNumberDoors;
	private int mCurrentLevelPlayerStartX;
	private int mCurrentLevelPlayerStartY;
	private Context mContext;
	
	private Monster[] mCurrentLevelMonstersAndSprites;
	private Door[] mCurrentLevelDoors;
	
	//TODO read from a separate file
	//0 is floor (also texture barrell)
	//1 bluestone
	//2 colorstone
	//3 eagle
	//4 greenlight
	//5 greystone
	//6 mossy
	//7 pillar
	//8 purplestone
	//9 redbrick
	//10 wood
	//11 barrel
	//12 pillar
	//13 purplestone
	//14 redbrick
	//15 wood
	
	private final int[] EXIT_X_POSITIONS = new int[] {8,8};
	private final int[] EXIT_Y_POSITIONS = new int[] {8,8};
	private final int[] NUMBER_OF_MONSTERS = new int[] {3,3};
	private final int[] NUMBER_OF_SPRITES = new int[] {3,3};
	
	private final int[] NUMBER_OF_DOORS = new int[] {2,2};
	//2.5, 1.5 is the real start, use 8.5, 3.5 for testing
	private final int[] PLAYER_START_X_POSITIONS = new int[] {(int) (2.5 * 64),(int) (2.5 * 64)};
	private final int[] PLAYER_START_Y_POSITIONS = new int[] {(int) (1.5 * 64),(int) (1.5 * 64)};
	
	//Note these positions must be replicated on the level maps
	private final int[] MONSTER_TYPES = new int[] {20,21,22};
	private final int[] MONSTER_START_X_POSITIONS = new int[] {2 * 64, 5 * 64, 8 * 64};
	private final int[] MONSTER_START_Y_POSITIONS = new int[] {8 * 64, 6 * 64, 2 * 64};
	private final int[] SPRITE_TYPES = new int[] {23,24,25};
	private final int[] SPRITE_START_X_POSITIONS = new int[] {3 * 64, 1 * 64, 2 * 64};
	private final int[] SPRITE_START_Y_POSITIONS = new int[] {5 * 64, 5 * 64, 4 * 64};
	
	public Level(int startLevelNumber, Context context) {
		
		mContext = context;
		mCurrentLevelNumber = startLevelNumber;
		loadLevel(mCurrentLevelNumber-1);
		
	}
	
	private synchronized void loadLevel(int levelNumber) {
		
		switch(levelNumber) {
		case 0: {
			mCurrentLevelMap = new int[][]  {
										  //X 0,  1,  2,  3,  4,  5,  6,  7,  8,  9      Y
											{ 1,  1,  1,  1,  1,  1,  1,  1,  1,  1 }, //0
								   			{ 1,  0,  0,  0,  1,  0,  1,  0,  0,  1 }, //1
								   			{ 1,  0,  0,  0,  1,  0,  0,  0, 22,  1 }, //2
								   			{ 1,  0,  0,  0,  1,  0,  0,  0,  0,  1 }, //3
								   			{ 1,  0,  0,  0,  1,  0,  1,  0,  0,  1 }, //4
								   			{ 1, 24,  0, 23,  1,  0,  1,  0,  0,  1 }, //5
								   			{ 1,  1, 18,  1,  1, 21,  0,  1,  0,  1 }, //6
								   			{ 1,  0,  0,  0,  0,  0,  0,  1,  0,  1 }, //7
								   			{ 1,  0, 20,  0,  0,  0,  0,  1, 19,  1 }, //8
								   			{ 1,  1,  1,  1,  1,  1,  1,  1,  3,  1 }  //9
								   			} ;
			break;
		}
		case 1: {
			mCurrentLevelMap = new int[][]  {
										   //X 0,  1,  2,  3,  4,  5,  6,  7,  8,  9      Y
											{ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 }, //0
											{ 10,  0,  0,  0, 10,  0, 10,  0,  0, 10 }, //1
											{ 10,  0,  0,  0, 10,  0,  0,  0, 22, 10 }, //2
											{ 10,  0,  0,  0, 10,  0,  0,  0,  0, 10 }, //3
											{ 10,  0,  0,  0, 10,  0, 10,  0,  0, 10 }, //4
											{ 10, 24,  0, 23, 10,  0, 10,  0,  0, 10 }, //5
											{ 10, 10, 18, 10, 10, 21,  0, 10,  0, 10 }, //6
											{ 10,  0,  0,  0,  0,  0,  0, 10,  0, 10 }, //7
											{ 10,  0, 20,  0,  0,  0,  0, 10, 19, 10 }, //8
											{ 10, 10, 10, 10, 10, 10, 10, 10,  3, 10 }  //9
											};
			break;
		}
		}
		
		
		mCurrentLevelExitX = EXIT_X_POSITIONS[levelNumber];
		mCurrentLevelExitY = EXIT_Y_POSITIONS[levelNumber];
		mCurrentLevelNumberMonstersAndSprites = NUMBER_OF_MONSTERS[levelNumber] + NUMBER_OF_SPRITES[levelNumber];
		mCurrentLevelNumberDoors = NUMBER_OF_DOORS[levelNumber];
		mCurrentLevelPlayerStartX = PLAYER_START_X_POSITIONS[levelNumber];
		mCurrentLevelPlayerStartY = PLAYER_START_Y_POSITIONS[levelNumber];

		loadMonstersAndSprites(levelNumber);
		loadDoors(levelNumber);
	}
	
	private void loadDoors(int levelNumber) {
        //initialise the array of doors
		mCurrentLevelDoors = new Door[mCurrentLevelNumberDoors];
		
        int currentDoor = 0;
        for(int x =0;x < MAP_WIDTH;x++) {
        	for(int y =0;y<MAP_HEIGHT;y++) {
        		if(mCurrentLevelMap[y][x] >= DOOR_START && mCurrentLevelMap[y][x] <= DOOR_END) {
        			
        			boolean exit = false;
        			if(y == mCurrentLevelExitY && x == mCurrentLevelExitX) {
        				exit = true;
        			}
        			
        			mCurrentLevelDoors[currentDoor] = new Door(mCurrentLevelMap[y][x],mContext,exit);
        			currentDoor++;
        		}
        	}
        }
		
	}

	private void loadMonstersAndSprites(int levelNumber) {

			//Initialise the array of monsters	        
			mCurrentLevelMonstersAndSprites = new Monster[mCurrentLevelNumberMonstersAndSprites];
			
			synchronized(mCurrentLevelMonstersAndSprites) {
				
			    int currentItem = 0;
				//First add the monsters, then the sprites
				for(int i = 0;i<NUMBER_OF_MONSTERS[levelNumber];i++) {
					mCurrentLevelMonstersAndSprites[currentItem] = new Monster(MONSTER_TYPES[i],MONSTER_START_X_POSITIONS[i],MONSTER_START_Y_POSITIONS[i],0,mContext);
					currentItem++;
				}
					
				for(int j = 0;j<NUMBER_OF_SPRITES[levelNumber];j++) {
					mCurrentLevelMonstersAndSprites[currentItem] = new Monster(SPRITE_TYPES[j],SPRITE_START_X_POSITIONS[j],SPRITE_START_Y_POSITIONS[j],0,mContext);
					currentItem++;
				}		
		}
	}
	
	public boolean isWall(int value) {
		return (value > FLOOR_VALUE && value < OBJECT_START);
	}
	
	public boolean isWall(int y, int x) {
		return isWall(mCurrentLevelMap[y][x]);
	}
	
	public boolean isDoor(int value) {
		return (value >= DOOR_START && value <= DOOR_END);
	}
	
	public boolean isDoor(int y, int x) {
		return isDoor(mCurrentLevelMap[y][x]);
	}
		
	public boolean isDoorOpen(int value) {		
		if(isDoor(value))
		{
			return mCurrentLevelDoors[value-DOOR_START].getState() == Door.DOOR_OPEN;
		} else {
			return false;
		}
	}
	
	public boolean isDoorOpen(int y, int x) {	
		return isDoorOpen(mCurrentLevelMap[y][x]);
	}	
	
	public boolean isDoorClosed(int value) {
		return mCurrentLevelDoors[value-DOOR_START].getState() == Door.DOOR_CLOSED;
	}
	
	public boolean isDoorClosed(int y, int x) {			
		return isDoorClosed(mCurrentLevelMap[y][x]);		
	}
	
	public boolean isDoorOpeningOrClosing(int y, int x) {		
		int value = mCurrentLevelMap[y][x];		
		return mCurrentLevelDoors[value-DOOR_START].getState() == Door.DOOR_OPENING || 
				mCurrentLevelDoors[value-DOOR_START].getState() == Door.DOOR_CLOSING;
	}
	
	public boolean isExitDoor(int value) {
		return mCurrentLevelDoors[value-DOOR_START].isExitDoor();
	}
	
	public boolean isSprite(int value) {
		return (value >= SPRITE_START && value <= SPRITE_END);
	}
	
	public boolean isSprite(int y, int x) {
		return isSprite(mCurrentLevelMap[y][x]);
	}
	
	public boolean isFloor(int y, int x) {
		return mCurrentLevelMap[y][x] == FLOOR_VALUE;
	}
	
	public boolean isSpritePickUp(int value) {
		if(isSprite(value))
		{
			return mCurrentLevelMonstersAndSprites[value-MONSTER_START].isPickUpItem();
		} else {
			return false;
		}
		
	}
	
	public boolean isMonster(int value) {
		return (value >= MONSTER_START && value <= MONSTER_END);
	}
	
	public boolean isMonsterAlive(int value) {
		if(isMonster(value)) {
			return mCurrentLevelMonstersAndSprites[value-MONSTER_START].getHealth() > 0;
		} else {
			return false;
		}
	}
	
	public boolean isMonsterAlive(int y, int x) {
		return isMonsterAlive(mCurrentLevelMap[y][x]);
	}
	
	public boolean isMonsterDead(int y, int x) {
		int value = mCurrentLevelMap[y][x];	
		if(isMonster(value)) {
			return mCurrentLevelMonstersAndSprites[value-MONSTER_START].getHealth() <= 0;
		} else {
			return false;
		}
	}
	
	public boolean isMonsterOrSprite(int y, int x) {
		int value = mCurrentLevelMap[y][x];
		return (value >= MONSTER_START && value <= SPRITE_END);
	}
	
	public int loadNextLevel() {
		
		mCurrentLevelNumber++;
		if(mCurrentLevelNumber > NUMBER_OF_LEVELS) {
			//End game! - for now reset to first level
			mCurrentLevelNumber = 1;
		}
		loadLevel(mCurrentLevelNumber-1);
		
		return mCurrentLevelNumber;
	}
	
	public int[][] getCurrentLevelMap() {
		return mCurrentLevelMap;
	}
	
	public int getCurrentLevelExitX() {
		return mCurrentLevelExitX;	
	}
	
	public int getCurrentLevelExitY() {
		return mCurrentLevelExitY;	
	}
	
	public int getCurrentLevelNumberOfMonstersAndSprites() {
		return mCurrentLevelNumberMonstersAndSprites;
	}
	
	public int getCurrentLevelNumberOfDoors() {
		return mCurrentLevelNumberDoors;
	}
	
	public Monster[] getCurrentLevelMonstersAndSprites() {
		synchronized(mCurrentLevelMonstersAndSprites) {
			return mCurrentLevelMonstersAndSprites;
		}
	}
	
	public Door[] getCurrentLevelDoors() {
		return mCurrentLevelDoors;
	}
	
	public int getCurrentLevelPlayerStartX() {
		return mCurrentLevelPlayerStartX;
	}
	
	public int getCurrentLevelPlayerStartY() {
		return mCurrentLevelPlayerStartY;
	}

	public void reset() {
		loadLevel(mCurrentLevelNumber-1);	
	}

	public void resetSeenForRaycast() {
		//reset the seen data for monsters, sprites and doors before a raycast loop

		// reset all the monsters and sprites - we have not seen them yet
        for (int i = 0;i < mCurrentLevelNumberMonstersAndSprites; i++) {
        	mCurrentLevelMonstersAndSprites[i].resetSeen();	        	
        }
		// reset all the doors - we have not seen them yet
        for (int i = 0;i < mCurrentLevelNumberDoors; i++) {
        	mCurrentLevelDoors[i].resetSeen();	        	
        }
		
	}

	public void destroy() {
        for (int i = 0;i < mCurrentLevelNumberMonstersAndSprites; i++) {
        	mCurrentLevelMonstersAndSprites[i].destroy();       	
        }
        for (int i = 0;i < mCurrentLevelNumberDoors; i++) {
        	mCurrentLevelDoors[i].destroy();       	
        }
		
	}

	public void startDoorOpen(int foundDoorNumber) {
		mCurrentLevelDoors[foundDoorNumber-DOOR_START].startOpen();		
	}

	public int getMonsterDamage(int foundMonsterNumber) {
		return mCurrentLevelMonstersAndSprites[foundMonsterNumber-MONSTER_START].getDamage();
	}

	public void setToFloor(int y, int x) {
		mCurrentLevelMap[y][x] = FLOOR_VALUE;		
	}	
}
